package ru.infosystem.server.dao.xml;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import ru.infosystem.client.data.Group;
import ru.infosystem.client.data.Student;
import ru.infosystem.server.dao.DaoGroup;
import java.sql.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Element;
import ru.infosystem.client.data.ServerException;

public class XmlDaoGroup implements DaoGroup {

    private Document doc;

    public XmlDaoGroup(Document doc) {
        this.doc = doc;
    }

    @Override
    public Group getByNum(int groupNumber) {
        ArrayList<Student> list = new ArrayList();
        Queue<Node> queue = new LinkedList<>();
        Node node = this.doc;
        String faculty = null;
        boolean empty;
        do {
            NamedNodeMap nnm = node.getAttributes();
            if (nnm != null) {
                for (int i = 0; i < nnm.getLength(); i++) {
                    Attr attr = (Attr) nnm.item(i);
                    if (node.getNodeName().equals("group") && attr.getValue().equals(Integer.toString(groupNumber))) {
                        list.addAll(getListOfStudentsInGroupNode(node, groupNumber));
                        faculty = node.getParentNode().getAttributes().item(0).getTextContent();
                        System.out.println("get by num");
                        return new Group(groupNumber, faculty, list); // нашли нужную группу, закончили
                    }
                }
            }

            NodeList l = node.getChildNodes();
            for (int i = 0; i < l.getLength(); i++) {
                queue.add(l.item(i));
            }

            empty = queue.isEmpty();
            if (!empty) {
                node = queue.poll();
            }
        } while (!empty);
        return null;

    }

    @Override
    public ArrayList<Student> getListOfStudents(int groupNumber) {

        ArrayList<Student> list = new ArrayList();
        Queue<Node> queue = new LinkedList<>();
        Node node = this.doc;
        boolean empty;
        do {
            NamedNodeMap nnm = node.getAttributes();
            if (nnm != null) {
                for (int i = 0; i < nnm.getLength(); i++) {
                    Attr attr = (Attr) nnm.item(i);
                    if (node.getNodeName() == "group" && attr.getValue().equals(Integer.toString(groupNumber))) {
                        list.addAll(getListOfStudentsInGroupNode(node, groupNumber));
                        System.err.println(list.size());
                        return list;
                    }
                }
            }

            NodeList l = node.getChildNodes();
            for (int i = 0; i < l.getLength(); i++) {
                queue.add(l.item(i));

            }

            empty = queue.isEmpty();
            if (!empty) {
                node = queue.poll();
            }

        } while (!empty);
        return list;
    }

    @Override // возвращает true, если удачно добавил
    public boolean addNewGroup(Group group) throws ServerException {
        if (this.getByNum(group.getGroupNumber()) != null) {
            throw new ServerException("Группа уже существует");
            //            return false; // такая группа уже существует
        }
        NodeList faculties = this.doc.getElementsByTagName("faculty");
        String groupFaculty = group.getFaculty();
        boolean facultyFound = false;
        int i;
        for (i = 0; i < faculties.getLength(); i++) {
            if (faculties.item(i).getAttributes().item(0).getTextContent().equals(groupFaculty)) { // если факультет нашли
                facultyFound = true;
            }
        }


        Element newGroupTag = this.doc.createElement("group");
        ArrayList<Student> list = group.getListOfStudent();
        if (list != null) { // если в группе есть студенты, а не пуста
            for (int s = 0; s < list.size(); s++) {
                // тег студента    
                Element newStudentTag = this.doc.createElement("student");
                // теги ФИО и даты    
                Element newSurnameTag = this.doc.createElement("surname");
                Element newNameTag = this.doc.createElement("name");
                Element newPatronymicTag = this.doc.createElement("patronymic");
                Element newAcceptanceTag = this.doc.createElement("acceptance");
                // вносим в теги ФИО и даты текстовые даные
                newSurnameTag.appendChild(this.doc.createTextNode(group.getListOfStudent().get(s).getSurname()));
                newNameTag.appendChild(this.doc.createTextNode(group.getListOfStudent().get(s).getName()));
                newPatronymicTag.appendChild(this.doc.createTextNode(group.getListOfStudent().get(s).getPatronymic()));
                newAcceptanceTag.appendChild(this.doc.createTextNode(group.getListOfStudent().get(s).getIncome().toString()));
                // прикрепляем теги к родителям
                newStudentTag.appendChild(newSurnameTag);
                newStudentTag.appendChild(newNameTag);
                newStudentTag.appendChild(newPatronymicTag);
                newStudentTag.appendChild(newAcceptanceTag);
                // надо проверить, чтобы айдишник студента был верным
                int id = Integer.valueOf(doc.getElementsByTagName("autoID").item(0).getTextContent()).intValue();
                // добавляется 2. ?
                newStudentTag.setAttribute("number", Integer.toString(id + 2));
                doc.getElementsByTagName("autoID").item(0).setTextContent(Integer.toString(id + 2));
                newGroupTag.appendChild(newStudentTag);
            }
        }
        newGroupTag.setAttribute("number", Integer.toString(group.getGroupNumber()));

        Element newFacultyTag = null;

        for (int c = 0; c < faculties.getLength(); c++) {
            if (faculties.item(c).getAttributes().item(0).getTextContent().equals(groupFaculty)) { // если факультет нашли
                i = c;
            }
        }
        if (facultyFound) {
            faculties.item(i).appendChild(newGroupTag); // просто добавляем к факультету новую группу
        } else { // создём факультет и приаттачиваем группу
            newFacultyTag = this.doc.createElement("faculty");
            newFacultyTag.setAttribute("number", groupFaculty);
            newFacultyTag.appendChild(newGroupTag);
            this.doc.getElementsByTagName("university").item(0).appendChild(newFacultyTag);
        }

        try {
            this.writeXml();

        } catch (Exception e) {
            System.err.println(e);
        }
        return true;
    }

    @Override
    public boolean deleteGroup(Group group) throws ServerException{
        if (this.getByNum(group.getGroupNumber()) == null) {
            throw new ServerException("В базе нет такой группы");
            //return false; // нет такой группы, удалять нечего
        }
        NodeList faculties = this.doc.getElementsByTagName("faculty");
        String groupFaculty = group.getFaculty();
        for (int i = 0; i < faculties.getLength(); i++) {
            if (faculties.item(i).getAttributes().item(0).getTextContent().equals(groupFaculty)) {
                NodeList listOfGroups = faculties.item(i).getChildNodes();
                for (int k = 0; k < listOfGroups.getLength(); k++) {
                    if (listOfGroups.item(k).getAttributes() != null) {
                        // если номер группы совпадает
                        if (listOfGroups.item(k).getAttributes().item(0).getTextContent().equals(Integer.toString(group.getGroupNumber()))) {
                            try {
                                faculties.item(i).removeChild(listOfGroups.item(k));
                                this.writeXml();
                                return true;
                            } catch (TransformerConfigurationException ex) {
                                Logger.getLogger(XmlDaoGroup.class.getName()).log(Level.SEVERE, null, ex);
                            } catch (TransformerException ex) {
                                Logger.getLogger(XmlDaoGroup.class.getName()).log(Level.SEVERE, null, ex);
                            }
                        }
                    }
                }
            }
        }
        return false;
    }

    private void writeXml() throws TransformerConfigurationException, TransformerException {
        DOMSource source = new DOMSource(this.doc);

        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        StreamResult result = new StreamResult("src/ru/infosystem/server/dao/xml/DataBase.xml");
        transformer.transform(source, result);

    }

    private ArrayList<Student> getListOfStudentsInGroupNode(Node group, int groupNumber) {
        ArrayList<Student> listOfStudentsInGroupNode = new ArrayList<>();
        NodeList listOfStudents = group.getChildNodes();
        for (int k = 0; k < listOfStudents.getLength(); k++) {
            NodeList listOfStudentData = listOfStudents.item(k).getChildNodes();
            if (listOfStudentData.getLength() == 9) { // magic. Remake it
                String surname = listOfStudentData.item(1).getTextContent();
                String name = listOfStudentData.item(3).getTextContent();
                String patronymic = listOfStudentData.item(5).getTextContent();
                String dateString = listOfStudentData.item(7).getTextContent().subSequence(0, 10).toString();
                Date date = Date.valueOf(dateString);
                listOfStudentsInGroupNode.add(new Student(surname, name, patronymic, groupNumber, date));
            }
        }

        return listOfStudentsInGroupNode;
    }

    public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException, TransformerConfigurationException, TransformerException {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document document = builder.parse("src/ru/infosystem/server/dao/xml/DataBase.xml");
        XmlDaoGroup xml = new XmlDaoGroup(document);

        //        DEBAG
//        ArrayList<Student> st = xml.getListOfStudents(6304);
//        for (int i = 0; i < st.size(); i++) {
//            System.out.println(st.get(i).getIncome().toString());
//        }

//        Group group = xml.getByNum(6304);
//        System.out.println(group.getListOfStudent().get(2).getGroupNumber());

        Group g = xml.getByNum(6304);
        System.out.println(g.getGroupNumber());
        // g.setGroupNumber(5);
        //xml.addNewGroup(g);
        //xml.deleteGroup(g);
    }
}
